#感知机学习算法对偶形式

import random
import numpy as np
import matplotlib.pyplot as plt


def sign(v):
    if v>=0:
        return 1
    else:
        return -1

def train(train_num,train_datas,lr):
    w = 0.0
    b = 0
    datas_len = len(train_datas)
    alpha = [0 for i in range(datas_len)]
    train_array = np.array(train_datas)
    #生成Gram矩阵
    gram = np.matmul(train_array[:,0:-1] , train_array[:,0:-1].T)
    for idx in range(train_num):
        tmp = 0
        i = random.randint(0,datas_len-1)
        yi = train_array[i,-1]
        #求w=∑ayx
        for j in range(datas_len):
            tmp = tmp + alpha[j]*train_array[j,-1]*gram[i,j]
        #求∑ayx+b
        tmp = tmp + b
        #判断是否需要更新参数
        if yi*tmp <= 0:
            # alpha=alpha+n（学习率）
            alpha[i] = alpha[i] + lr
            # b=b+ny
            b = b + lr*yi
    # w=∑ayx
    for i in range(datas_len):
        w = w + alpha[i]*train_array[i,0:-1]*train_array[i,-1]
    return w,b,alpha,gram

#获得感知机模型的函数表达式
def get_perceptron(w,b):
    return "f(x)=sign("+str(w[0])+"*x1+("+str(w[1])+")*x2+("+str(b)+"))"

def plot_points(train_datas,w,b):
    plt.figure()
    x1 = np.linspace(0, 8, 100)
    x2 = (-b-w[0]*x1)/(w[1]+1e-10)
    plt.plot(x1, x2, color='r', label='y1 data')
    datas_len=len(train_datas)
    for i in range(datas_len):
        if(train_datas[i][-1]==1):
            plt.scatter(train_datas[i][0],train_datas[i][1],marker='o',s=100)
        else:
            plt.scatter(train_datas[i][0],train_datas[i][1],marker='x',s=100)
    plt.show()

if __name__=='__main__':
    train_data1 = [[3, 3, 1], [4, 3, 1]]  # 正样本
    train_data2 = [[1, 1, -1]]  # 负样本
    train_datas = train_data1 + train_data2  # 样本集
    w,b,alpha,gram=train(train_num=50,train_datas=train_datas,lr=1)
    print(get_perceptron(w, b))  # 输出函数表达式
    plot_points(train_datas,w,b)